package com.tarena.test.rabbitmq.simple;

import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import org.junit.Before;
import org.junit.Test;

/**
 * 模拟一个rabbitmq可以实现的单发 单接的简单模式
 */
public class SimpleMode {
    //构造一个连接对象 短链接对象
    private Channel channel;
    //在测试单元中 所有@Test测试方法执行之前 运行
    //初始化当前类中的channel成员变量
    @Before
    public void initChannel() throws IOException, TimeoutException {
        //这里方法 创建长连接 连接本地rabbtimq 从而获取一个短链接
        //使用连接工厂 创建连接
        ConnectionFactory factory=new ConnectionFactory();
        //localhost:5672 15672是http协议 5672是tcp/ip guest/guest virtual_host /
        factory.setHost("localhost");
        factory.setPort(5672);
        factory.setUsername("guest");
        factory.setPassword("guest");
        factory.setVirtualHost("/");
        //从连接工厂拿到 连接对象
        Connection connection= factory.newConnection();
        //长连接对象获取创建短链接
        channel=connection.createChannel();
    }
    //声明一个测试使用的队列
    @Test
    public void declareQueue() throws IOException {
        System.out.println("开始声明队列");
        //声明一个rabbtimq的队列 ,需要填补队列的参数5个
        //1.队列名称 String字符串 simple-q
        //2.队列是否持久化 boolean布尔 true队列会被持久化到磁盘 false rabbitmq停止后队列会丢失
        //3.队列是否专属 boolean布尔 true专属 false不专属 如果是专属队列,只有创建声明队列的connection连接可以操作队列
        //4.是否自动删除 boolean布尔 true使用队列的连接一旦消失,队列自动删除,反之不会删除
        //5.其他参数 Map<String,Object> 可以定义rabbitmq一些队列特性 比如死信队列 延迟队列 队列最大消息数量等
        //我要声明一个 支持持久化 不专属 不会自动删除 没有其他参数的 叫做simple-q队列
        channel.queueDeclare("simple-q1",false,false,false,null);
        //如果第一次声明 就是创建 如果已经存在 则没有效果
        System.out.println("队列声明完毕");
    }
    //生产者 发送消息
    @Test
    public void sendMessage() throws IOException {
        System.out.println("生产者即将发送");
        //1.准备一个发送的消息数据
        String msg="这是我的第一条消息";
        //2.调用链接api方法 像rabbitmq发送消息
        channel.basicPublish("","simple-q",null,msg.getBytes());
        //发送消息的时候 方法的4个参数表示
        //a.交换机名称 String字符串 "" 是一个特殊的交换机 不指定交换使用的是默认交换机
        //b.路由key值 String字符串 这里是目标队列名称
        //c.消息携带的属性 userId发送人id appId发送者所在的应用id等等
        //d.消息体,byte[] 如果我们想要发送的是对象数据,需要将对象序列化成二进制byte数组
        System.out.println("生产者发送结束");
    }
    //消费者监听消费
    @Test
    public void consumer() throws IOException {
        //消费调用之前 将2个回调队形创建
        DeliverCallback deliverCallback=new DeliverCallback() {
            //消费者拿到消息后的 核心消费逻辑 就是在handle完成的
            //consumerTag 消费者标签 当前消费者id 唯一的标识一个消费者进程
            //message 就是生产者发送的那个消息对象 包含消息体byte properties headers
            @Override public void handle(String consumerTag, Delivery message) throws IOException {
                //测试环境中 可以打印一些数据
                System.out.println("当前消费者tag:"+consumerTag);
                //从消息中解析数据 打印
                byte[] body = message.getBody();
                String msg=new String(body);
                System.out.println("消费者接收到消息:"+msg);
                //每次确定消费完毕之后 确认消息
                //这里的确认 要使用消息id 消息对象的deliveryTag标签 是否要批量确认
                channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
            }
        };
        CancelCallback cancelCallback=new CancelCallback() {
            @Override public void handle(String consumerTag) throws IOException {
                //TODO
            }
        };
        //1.消费者监听绑定队列
        channel.basicConsume("simple-q",false,deliverCallback,cancelCallback);
        //参数解释
        //a .队列名称
        //b .是否自动确认 true如果是自动确认 消费的速度比较快 false手动确认 没有经过手动确认 消息不会删除 消息转改 unack消费未确认
        //c .消息处理的逻辑 执行消费逻辑
        //d .在消费过程中 如果消费者没有执行完消费逻辑,被删除销毁了 cancelCallback会被调用
        //为了清楚的看到消费代码的结果 让消费进程不停止
        while(true);
    }
}
